#include "dirent.h"
#include <sys/dir.h>		// local directory structure
#include <sys/stat.h>		// structure returned by stat
#include <sys/types.h>		// typedefs
#include <fcntl.h>		// flags for read and write
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// fsize: print the name of file "name"
void fsize(char *name)
{
	struct stat stbuf;

	if(stat(name, &stbuf) == -1)
	{
		fprintf(stderr, "fsize: can't access %s\n", name);
		return ;
	}
	if((stbuf.st_mode & S_IFMT) == S_IFDIR)
		dirwalk(name, fsize);
	printf("%8ld %s\n", stbuf.st_size, name);
}

// dirwalk: apply fcn to all files in dir
void dirwalk(char *dir, void (*fun)(char *))
{
	char name[MAX_PATH];
	Dirent *dp;
	myDIR *dfd;

	if((dfd = myopendir(dir)) == NULL)
	{
		fprintf(stderr, "dirwalk: can't open %s\n", dir);
		return ;
	}
	while((dp = myreaddir(dfd)) != NULL)
	{
		if(strcmp(dp->name, ".") == 0 || strcmp(dp->name, "..") == 0)
			continue ;		// skip self and parent
		if(strlen(dir) + strlen(dp->name) + 2 > sizeof(name))
			fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->name);
		else
		{
			sprintf(name, "%s/%s", dir, dp->name);
			(*fun)(name);
		}
	}
	myclosedir(dfd);
}

// opendit: open a directory for readdir calls
myDIR *myopendir(char *dirname)
{
	int fd;
	struct stat stbuf;
	myDIR *dp;

	if((fd = open(dirname, O_RDONLY, 0)) == -1 
		|| fstat(fd, &stbuf) == -1
		|| (stbuf.st_mode & S_IFMT) != S_IFDIR
		|| (dp = (myDIR *)malloc(sizeof(myDIR))) == NULL)
			return NULL;
	dp->fd = fd;
	return dp;
	
}

// closedir: close directory opened by opendir
void myclosedir(myDIR *dp)
{
	if(dp)
	{
		close(dp->fd);
		free(dp);
	}
}

// readdir: read directory entries in sequence
Dirent *myreaddir(myDIR *dp)
{
	struct direct dirbuf;		// local directoty structure
	static Dirent d;		// return: portable structure

	while(read(dp->fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf))
	{
		if(dirbuf.d_ino == 0)	// slot not in use
			continue;
		d.ino = dirbuf.d_ino;
		strncpy(d.name, dirbuf.d_name, DIRSIZ);
		d.name[DIRSIZ] = '\0';
		return &d;
	}	
	return NULL;
}
	
